package basic.arch.component.lock.local;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import basic.arch.component.lock.config.CurrentLockSettings;
public class LocalLock {

    private static final ConcurrentHashMap<String, ReentrantLock> LOCK_MAP = new ConcurrentHashMap<String, ReentrantLock>();

    private static final Logger LOGGER = LoggerFactory.getLogger(LocalLock.class);

    /**
     * 锁的全局配置
     */
    private LockSettings lockSettings;

    public LocalLock(LockSettings lockSettings) {
        super();
        this.lockSettings = lockSettings;
    }

    /**
     * @param lockSettings
     *            锁的相关设置---每个锁的key都对应一个自己的settings
     */
    public LocalLockResult tryLock(CurrentLockSettings currentLockSettings) {
        // 1. 根据key值获取ReentrantLock
        String currenLockKey = currentLockSettings.getCurrenLockKey();
        //多种类型并发锁必须加类型
        currenLockKey=currentLockSettings.getDisstributionLockType().name()+currenLockKey;
        
        ReentrantLock reentrantLock = LOCK_MAP.get(currenLockKey);
        if (reentrantLock == null) {
            reentrantLock = new ReentrantLock(true);
            // 没有key对应的ReentrantLock，写入新的ReentrantLock，并发场景下用putIfAbsent，用
            // putIfAbsent 方法时切记要对返回值进行判断
            ReentrantLock existed = LOCK_MAP.putIfAbsent(currenLockKey, reentrantLock);
            if (existed != null) {
                reentrantLock = existed;
            }
        }
        LocalLockResult localLockResult = new LocalLockResult();
        localLockResult.setLock(reentrantLock);
        localLockResult.setCurrentLockSettings(currentLockSettings);
        
        long start = System.currentTimeMillis();
        if (reentrantLock.getQueueLength() <= lockSettings.getSingleWaitThreshold()) {
            int lockMaxWaitTime = currentLockSettings.getLocalLockMaxWaitTime();
            try {
                if ( lockMaxWaitTime> 0) {
                    int tryTimes = currentLockSettings.getTryTimes();
                    for(int i = 0;i<tryTimes+1;i++){
                        if (reentrantLock.tryLock(lockMaxWaitTime, TimeUnit.MILLISECONDS)) {
                            localLockResult.setLocalLockSuccess(true);
                            break;
                        }else{
                            if(i!=0){
                                LOGGER.warn("获取本地锁失败，进行第{}次重试",i); 
                            }
                        }
                    }
                } else {
                    reentrantLock.lock();
                    localLockResult.setLocalLockSuccess(true);
                }
            } catch (InterruptedException e) {
                LOGGER.error("获取本地锁key:{} in {} miliseconds 发生中断", currenLockKey, lockMaxWaitTime);
            }
        }else{
            LOGGER.warn("本地锁阀值已达上限,目前大小:{},设置的最大阀值是:{}",reentrantLock.getQueueLength(),lockSettings.getSingleWaitThreshold());
        }
        long end=System.currentTimeMillis();
        localLockResult.setLocalLockTime(end-start);
        return localLockResult;
    }

    public void unlock(LocalLockResult localLockResult) {
        try {
            if (localLockResult != null && localLockResult.getLock() != null) {
                if (localLockResult.isLocalLockSuccess()) {
                    localLockResult.getLock().unlock();
                }
                // 当前key没有线程等待锁，删除
                if (!localLockResult.getLock().hasQueuedThreads()) {
                    LOCK_MAP.remove(localLockResult.getCurrentLockSettings().getCurrenLockKey(), localLockResult.getLock());
                }
            }
        } catch (Exception e) {
            LOGGER.error("key:{},释放本地锁发生错误 ,{}", localLockResult.getCurrentLockSettings().getCurrenLockKey(), e);
        }
    }
}
